package com.pucong.gitee.database.backup.business.sys.service;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.pucong.gitee.database.backup.business.sys.cover.SysFileBeanConvert;
import com.pucong.gitee.database.backup.business.sys.mapper.SysConfigMapper;
import com.pucong.gitee.database.backup.business.sys.mapper.SysFileMapper;
import com.pucong.gitee.database.backup.common.entity.sys.*;
import com.pucong.gitee.database.backup.common.selfenum.FileStatusEnum;
import com.pucong.gitee.database.backup.common.selfenum.SysFileTypeMesh;
import com.pucong.gitee.database.backup.configuration.exception.BusinessException;
import com.pucong.gitee.database.backup.utils.*;
import com.pucong.gitee.database.backup.utils.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 附件操作 service实现类
 *
 * @author pucong
 * @company gitee.com/pucong
 * @create 2020-12-02
 */
@Service
@Slf4j
public class SysFileServiceImpl implements SysFileService {

    @Autowired
    private SysFileMapper sysFileMapper;

    @Autowired
    private SysConfigMapper sysConfigMapper;

    @Autowired
    private SysFileBeanConvert sysFileBeanConvert;

    @Override
    public SysFileSelectVo getSysFileById(String id) {
        SysFile sysFile = sysFileMapper.selectById(id);
        return sysFileBeanConvert.do2vo(sysFile);
    }

    @Override
    public List<SysFileSelectVo> getSysFileByBusinessId(String businessId) {
        LambdaQueryWrapper<SysFile> sysFileParams = new LambdaQueryWrapper<>();
        sysFileParams.eq(SysFile::getBusinessId, businessId);
        List<SysFile> sysFiles = sysFileMapper.selectList(sysFileParams);
        List<SysFileSelectVo> sysFileSelectVos = sysFileBeanConvert.doList2vo(sysFiles);
        return sysFileSelectVos;
    }

    @Override
    public SysFileUploadVo uploadFile(MultipartFile file, SysFileUploadDto dto) {
        UserJwt loginUser = UserRequest.getLoginUser();
        // 获取文件名
        String fileNameOld = file.getOriginalFilename();
        if (fileNameOld.lastIndexOf(".") <= 0) {
            throw new BusinessException("上传文件后缀错误，请设置后缀再上传:" + fileNameOld);
        }
        // 去掉文件夹名称
        int i = fileNameOld.indexOf("/") + 1;
        if (i > 0) {
            fileNameOld = fileNameOld.substring(i);
        }
        // 获取文件的后缀名
        String suffixName = fileNameOld.substring(fileNameOld.lastIndexOf("."));
        // 解决中文问题，liunx下中文路径，图片显示问题
        String fileName = UUID.getSerializNo() + UUID.uuid() + suffixName;

        // 设置上传路径
        String path = ApplicationContextUtil.getTmpPath() + File.separator + fileName;
        // 上传
        FileUtil.uploadFile(file, path);
        // 插入附件信息
        SysFile sysFile = new SysFile();
        sysFile.setUuid(UUID.uuid());
        sysFile.setFileName(fileName);
        sysFile.setFileNameOld(fileNameOld);
        sysFile.setFileSize(file.getSize() + "");
        sysFile.setFileType(suffixName);
        sysFile.setPath(path);
        sysFile.setCreateBy(loginUser.getUuid());
        sysFile.setCreateTime(new Date());
        if (StringUtils.isBlank(dto.getBusinessId())) {
            sysFile.setFileStatus(FileStatusEnum.临时文件);
        } else {
            sysFile.setBusinessId(dto.getBusinessId());
            sysFile.setFileStatus(FileStatusEnum.正式文件);
        }
        sysFileMapper.insert(sysFile);

        SysFileUploadVo vo = sysFileBeanConvert.do2UploadVo(sysFile);
        return vo;
    }

    @Override
    public int deleteFileById(String id) {
        SysFile sysFile = sysFileMapper.selectById(id);
        deleteFile(id, sysFile.getPath());
        return 1;
    }

    @Override
    public int deleteFileByIdList(List<String> idList) {
        List<SysFile> sysFiles = sysFileMapper.selectBatchIds(idList);
        sysFileMapper.deleteBatchIds(idList);
        for (SysFile sysFile : sysFiles) {
            // 删除文件
            FileUtil.deleteFile(sysFile.getPath());
        }
        return 0;
    }

    private void deleteFile(String id, String path) {
        int i = sysFileMapper.deleteById(id);
        // 删除文件
        FileUtil.deleteFile(path);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int deleteFileByBusinessId(String businessId) {
        LambdaQueryWrapper<SysFile> selectParams = new LambdaQueryWrapper<>();
        selectParams.eq(SysFile::getBusinessId, businessId);
        selectParams.eq(SysFile::getFileStatus, FileStatusEnum.正式文件);
        List<SysFile> sysFiles = sysFileMapper.selectList(selectParams);
        for (SysFile sysFile : sysFiles) {
            deleteFile(sysFile.getUuid(), sysFile.getPath());
        }
        return sysFiles.size();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int updateDocChangeStatus(ChangeFileStatusVo changeFileStatusVo) {
        List<String> fileIdList = changeFileStatusVo.getFileIdList();
        if (fileIdList == null || fileIdList.size() == 0) {
            throw new BusinessException("传入参数错误");
        }
        if (StringUtils.isBlank(changeFileStatusVo.getBusinessId())) {
            throw new BusinessException("传入参数错误");
        }
        // 获取文件信息，对比找出需要删除的文件
        LambdaQueryWrapper<SysFile> selectParams = new LambdaQueryWrapper<>();
        selectParams.eq(SysFile::getBusinessId, changeFileStatusVo.getBusinessId());
        List<SysFile> sysFiles = sysFileMapper.selectList(selectParams);
        List<String> collect = sysFiles.stream().map(SysFile::getUuid).collect(Collectors.toList());
        collect.removeAll(fileIdList);
        for (String fileId : collect) {
            deleteFileById(fileId);
        }
        // 复制文件到正式目录
        for (String fileId : fileIdList) {
            SysFile sysFile = sysFileMapper.selectById(fileId);
            if(sysFile.getFileStatus() != FileStatusEnum.正式文件){
                String path = ApplicationContextUtil.getUploadPath() + File.separator + sysFile.getFileName();
                File oldName = new File(sysFile.getPath());
                File newName = new File(path);
                oldName.renameTo(newName);
            }
        }
        SysFile update = new SysFile();
        update.setBusinessId(changeFileStatusVo.getBusinessId());
        update.setFileStatus(FileStatusEnum.正式文件);
        LambdaQueryWrapper<SysFile> updateParams = new LambdaQueryWrapper<>();
        updateParams.in(SysFile::getUuid, fileIdList);
        int i = sysFileMapper.update(update, updateParams);
        return i;
    }

    @Override
    public void downLoadFileById(String uuid, HttpServletRequest request, HttpServletResponse response) {
        SysFile sysFile = sysFileMapper.selectById(uuid);
        if (sysFile == null) {
            throw new BusinessException("传入参数错误");
        }
        FileUtil.downLoadFile(sysFile.getPath(), sysFile.getFileNameOld(), response);
    }

    @Override
    public void lookFileById(String uuid, HttpServletRequest request, HttpServletResponse response) {
        SysFile sysFile = sysFileMapper.selectById(uuid);
        if (sysFile == null) {
            throw new BusinessException("传入参数错误");
        }
        FileUtil.lookFile(sysFile.getPath(), sysFile.getFileNameOld(), response);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteTmpFile() {
        LambdaQueryWrapper<SysFile> selectParams = new LambdaQueryWrapper<>();
        selectParams.eq(SysFile::getFileStatus, FileStatusEnum.临时文件);
        List<SysFile> sysFiles = sysFileMapper.selectList(selectParams);
        for (SysFile sysFile : sysFiles) {
            deleteFile(sysFile.getUuid(), sysFile.getPath());
        }
        // 删除缓存文件
        String path = ApplicationContextUtil.getTmpPath();
        log.info("删除缓存文件：" + path);
        File file = new File(path);
        FileUtil.delFile(file);
    }

    @Override
    public SysFileUploadVo uploadBigFileChunkExp(MultipartFile file, BigFileUploadParamDto param) {
        UserJwt loginUser = UserRequest.getLoginUser();
        int chunk = param.getChunk();
        String fileNameOld = param.getFileName();

        // 获取文件的后缀名
        String suffixName = "";
        int suffixIndex = fileNameOld.lastIndexOf(".");
        if(suffixIndex > 0){
            suffixName = fileNameOld.substring(suffixIndex + 1);
        }
        // 解决中文问题，liunx下中文路径，图片显示问题
        String fileNameNew = UUID.getSerializNo() + java.util.UUID.randomUUID() + suffixName;
        log.info("收到文件分块:" + fileNameOld + ":" + chunk);
        log.info("收到当前块大小:" + param.getSize() + "----总文件大小：" + param.getFileSize());
        log.info("收到当前块MD5:" + param.getFileMd5() + "----总文件MD5：" + param.getChunk());
        SysFile sysFile = new SysFile();
        sysFile.setUuid(param.getFileMd5() + "_" + param.getChunk());
        sysFile.setBusinessId(param.getFileMd5());
        sysFile.setFileNameOld(fileNameOld);
        sysFile.setFileType(suffixName);
        sysFile.setFileSize(param.getSize() + "");
        sysFile.setCreateTime(new Date());
        sysFile.setCreateBy(loginUser.getUuid());
        sysFile.setFileName(fileNameNew);
        sysFile.setFileStatus(FileStatusEnum.临时文件);

        String uploadPath = ApplicationContextUtil.getTmpPath() + File.separator + fileNameNew;
        SysFile old = sysFileMapper.selectById(param.getFileMd5() + "_" + param.getChunk());
        // 上传
        if (old == null) {
            FileUtil.uploadFile(file, uploadPath);
        }

        old = sysFileMapper.selectById(param.getFileMd5() + "_" + param.getChunk());
        if (old == null) {
            sysFile.setPath(uploadPath);
            sysFileMapper.insert(sysFile);
        }
        LambdaQueryWrapper<SysFile> query = new LambdaQueryWrapper<>();
        query.eq(SysFile::getBusinessId, param.getFileMd5())
                .eq(SysFile::getFileStatus, FileStatusEnum.临时文件);
        List<SysFile> sysFiles = sysFileMapper.selectList(query);

        //是否是最后一块，将文件信息保存数据库
        if (sysFiles.size() == param.getChunkTotal()) {
            SysFile insertSysFile = new SysFile();
            insertSysFile.setUuid(UUID.uuid());
            if (StringUtils.isBlank(param.getBusinessId())) {
                insertSysFile.setFileStatus(FileStatusEnum.临时文件);
            } else {
                insertSysFile.setBusinessId(param.getBusinessId());
                insertSysFile.setFileStatus(FileStatusEnum.正式文件);
            }
            insertSysFile.setFileNameOld(fileNameOld);
            insertSysFile.setFileType(SysFileTypeMesh.NOT_MESH.getValue());
            insertSysFile.setFileSize(param.getFileSize());
            insertSysFile.setCreateTime(new Date());
            insertSysFile.setCreateBy(loginUser.getUuid());
            insertSysFile.setFileName(fileNameNew);
            // 合并到目录
            String fileNameFirst = UUID.getSerializNo() + java.util.UUID.randomUUID() + "." + suffixName;
            String uploadPathFirst = ApplicationContextUtil.getUploadPath() + File.separator + fileNameFirst;
            insertSysFile.setPath(uploadPathFirst);
            sysFileMapper.insert(insertSysFile);


            log.info("合并文件开始");
//            ThreadPoolUtils.executor(() -> {
                try {
                    // 创建空文件
                    File meshFile = new File(uploadPathFirst);
                    if(!meshFile.getParentFile().exists()){
                        meshFile.getParentFile().mkdirs();
                    }
                    if(!meshFile.exists()){
                        meshFile.createNewFile();
                    }
                    Map<Integer, SysFile> map = this.changeSysFileMap(sysFiles);
                    try {
                        FileChannel resultFileChannel = new FileOutputStream(meshFile, true).getChannel();
                        for (int i = 0; i < param.getChunkTotal(); i++) {
                            SysFile sysFile1 = map.get(i);
                            File meshFile3 = new File(sysFile1.getPath());
                            FileChannel inChannel = new FileInputStream(meshFile3).getChannel();
                            resultFileChannel.transferFrom(inChannel, resultFileChannel.size(), inChannel.size());
                            inChannel.close();
                        }
                        resultFileChannel.close();
                    } catch (IOException e) {
                        log.error("合并文件失败" + e.getMessage());
                        sysFileMapper.deleteById(insertSysFile.getUuid());
                        throw new BusinessException("合并文件失败");
                    }
                    log.info("合并文件完成");
                    //保存到数据库
                    SysFile updateSysFile = new SysFile();
                    updateSysFile.setUuid(insertSysFile.getUuid());
                    updateSysFile.setFileType(suffixName);
                    sysFileMapper.updateById(updateSysFile);
                    log.info("文件上传成功");
                    //删除缓存
                    for (SysFile sysFile1 : sysFiles) {
                        File file1 = new File(sysFile1.getPath());
                        file1.delete();
                    }
                    LambdaQueryWrapper<SysFile> deleteParam = new LambdaQueryWrapper<>();
                    deleteParam.eq(SysFile::getBusinessId, param.getFileMd5());
                    sysFileMapper.delete(deleteParam);
                } catch (Exception e) {
                    log.error("文件上传失败", e);
                }
//            });
            SysFileUploadVo vo = sysFileBeanConvert.do2UploadVo(sysFile);
            vo.setUuid(insertSysFile.getUuid());
            vo.setFileSize(param.getFileSize());
            vo.setBusinessId("");
            vo.setUploadSucceed(true);
            return vo;
        } else {
            SysFileUploadVo vo = new SysFileUploadVo();
            vo.setFileType(suffixName);
            vo.setFileSize(param.getFileSize());
            vo.setFileNameOld(param.getFileName());
            vo.setUploadSucceed(false);
            return vo;
        }

    }

    private Map<Integer, SysFile> changeSysFileMap(List<SysFile> sysFileList) {
        Map<Integer, SysFile> sysFileMap = new HashMap(sysFileList.size());
        for (SysFile sysFile : sysFileList) {
            Integer index = Integer.parseInt(sysFile.getUuid().substring(sysFile.getUuid().indexOf("_") + 1));
            sysFileMap.put(index, sysFile);
        }
        return sysFileMap;
    }
}
